//
//  appController.m
//  websterEnglish
//
//  Created by Wallace Wu on 09-2-5.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "appController.h"
#import "VOCWordData.h"
#import "VOCWebView.h"

@implementation appController

-(void)log:(NSString*)string
{
    //[output insertText:[string stringByAppendingString:@"\n"]];
    NSLog(@"%@",string);
}

-(void)error:(NSString*)string
{
    [error insertText:[string stringByAppendingString:@"\n"]];
}

+(NSString*)dictPath
{
    NSString *resource = [[NSBundle mainBundle] resourcePath];
    NSString *dictFolder = [resource stringByAppendingPathComponent:@"Dict"];
    return [dictFolder stringByAppendingPathComponent:@"HongBao2007"];
}

-(void)awakeFromNib
{
    NSString *path = [appController dictPath];
    retry = [[NSMutableArray alloc] init];
    vocHongBao = [[NSKeyedUnarchiver unarchiveObjectWithFile:path] retain];
    NSLog(@"have %d words to process", [vocHongBao count]);
    memset(flags, NO, 10*sizeof(BOOL));
}

-(void)dealloc
{
    [retry  release];
    [vocHongBao release];
    [super dealloc];
}

-(IBAction)start:(id)sender
{
    //    for (NSString *word in [vocHongBao allKeys]) {
    //        NSCharacterSet *set = [[NSCharacterSet lowercaseLetterCharacterSet] invertedSet];
    //        NSString *eng = [[vocHongBao objectForKey:word] word];
    //        NSRange range = [eng rangeOfCharacterFromSet:set];
    //        if(range.location != NSNotFound)
    //        {
    //            NSLog(@"the word %@ is not valid", word);
    //        }
    //    }
    for (int i = 1; i <= 10; i++) {
        [NSThread detachNewThreadSelector:@selector(load:) toTarget:self withObject:[NSNumber numberWithInt:i]];
    }
    [NSThread detachNewThreadSelector:@selector(retry:) toTarget:self withObject:[NSNumber numberWithInt:0]];
}

-(void)retry:(NSNumber *)threadId
{
    NSAutoreleasePool *pool;
    while (YES) {
        pool = [[NSAutoreleasePool alloc] init];
        while ([retry count] == 0) {
            [NSThread sleepForTimeInterval:1];
        }
        
        //processing failed words
        for (RetryBlock *word in retry) {
            if(word.times ==5)
            {
                [self error:[NSString stringWithFormat:@"did not find the explanations for %@", word.word]];
                word.times++;                    
            }
            else if(word.times < 5)
            {
                word.times++;
                [self fetchWord:word.word inThread:[threadId intValue]];
            }
        }
        //        @synchronized(self)
        //        {
        //            [retry removeObjectsInArray:toBeRemoved];
        //            [toBeRemoved release];
        //        }
        
        
        [NSThread sleepForTimeInterval:1];
        [pool release];
    }
    
}

-(void)load:(id)obj
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    int threadId = [obj intValue];
    [NSThread sleepForTimeInterval:.5*threadId];
    flags[threadId] = NO;
    int count;
    if(threadId!=10)
    {
        count = [vocHongBao count]/10;
    }
    else
    {
        count = [vocHongBao count]/10 +[vocHongBao count]%10;
    }
    
    NSLog(@"the thread %d proceeds %d words", threadId, count);
    int index = ([vocHongBao count]/10) * (threadId-1);
    for (int i = 0; i < count; i++) {
        VOCWordData *word = [[vocHongBao allValues] objectAtIndex:index+i];
        if(!word)
        {
            [self error:[NSString stringWithFormat:@"@Thread %d: Did not find required word",threadId]];
            return;
        }
        [self log:[NSString stringWithFormat:@"going to process word %@",[word word]]];   
        [self fetchWord:[word word] inThread:threadId];
        flags[threadId] = YES;
        
        while (flags[threadId]) {
            [NSThread sleepForTimeInterval:1];
        } 
    }
    NSLog(@"thread %d complete", threadId);
    [pool release];
}

-(VOCWebView*)webViewOnMainThread
{
    VOCWebView *view = [[VOCWebView alloc] initWithFrame:NSMakeRect(0, 0, 0, 0) frameName:nil groupName:nil];
    return view;
}

-(void)fetchWord:(NSString*)word inThread:(int)threadId;
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    VOCWebView *view = nil;
    NSMethodSignature *sig = [self methodSignatureForSelector:@selector(webViewOnMainThread)];
    NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:sig];
    [invoc setTarget:self];
    [invoc setSelector:@selector(webViewOnMainThread)];
    [invoc performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];
    [invoc getReturnValue:&view];
    [view setWord:word];
    [view setThreadID:threadId];
    
    if(!view)
    {
        [self error:[NSString stringWithFormat:@"@Thread %d: fatal, can not create the view",threadId]];
        return;
    }
    
    [view setFrameLoadDelegate:self];
    
    NSString *url = [NSString stringWithFormat:@"http://www.merriam-webster.com/wordclick/%@",word];
    url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *tmp = [NSURL URLWithString:url];
    if(!tmp)
    {
        [self error:[NSString stringWithFormat:@"@Thread %d:fatal error the url for word %@ is nil",threadId,word]];
        return;
    }
    //[[view mainFrame] loadRequest:[NSURLRequest requestWithURL:tmp]];
    [[view mainFrame] performSelectorOnMainThread:@selector(loadRequest:) withObject:[NSURLRequest requestWithURL:tmp] waitUntilDone:NO];
    
    [pool release];
}

- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
	DOMDocument *myDOMDocument = [[sender mainFrame] DOMDocument];
    if(myDOMDocument)
    {
        [self log:[NSString stringWithFormat:@"successfully received the response for %@",[sender mainFrameURL]]];
    }
    else
    {
        [self error:@"failed to fetch the server`s response"];
        return;
    }
    [self log:@"begin to parse"];
    
    //as long as we will be abel to have to receive only one kind of word`s explanation in a seperate web request, we need to parse other kinds` location and parse them
    //TODO: to be improved
    DOMDocument *doc = [frame DOMDocument];
    NSString *word =[(VOCWebView*)sender word];
    
    //find the list and explanations   
    BOOL found = NO;
    DOMNodeList *defs = [doc getElementsByTagName:@"div"];
    int idx;
    for (idx=0; idx<[defs length]; idx++) {
        DOMHTMLDivElement *object= (DOMHTMLDivElement*)[defs item:idx];
        if([[object className] isEqualToString:@"defs"])
        {
            found = YES;
            @synchronized(self)
            {
                for (int i = 0; i < [retry count]; i++) {
                    if([[(RetryBlock*)[retry objectAtIndex:i] word] isEqualToString:[(VOCWebView*)sender word]])
                    {
                        [retry removeObjectAtIndex:i];
                    }
                }
            }
            
            //process found explanation
            
        }
        if([[object className] isEqualToString:@"page_results"])
        {
            //find if there is related word
            DOMNodeList *lists = [object getElementsByTagName:@"li"];
            for (int i = 0; i < [lists length]; i++) {
                DOMHTMLLIElement *lis = (DOMHTMLLIElement*)[lists item:i];
                DOMNodeList *links = [lis getElementsByTagName:@"a"];
                DOMHTMLLinkElement *link = (DOMHTMLLinkElement*)[links item:0];
                NSString* destUrl = [link href];
                //match the url if it is a valid url for this word`s other functions
                NSString *stringToMatch = [NSString stringWithFormat:@"/wordclick/%@[",word];
                NSRange result = [destUrl rangeOfString:stringToMatch];
                
                //this is a valid link
                if(result.location != NSNotFound)
                {
                    NSString *newWord = [destUrl substringFromIndex:41];
                    if(![newWord isEqualToString:[(VOCWebView*)sender word]])
                    {
                        [self fetchWord:newWord inThread:[(VOCWebView*)sender threadID]];                        
                    }
                }
            }
            @synchronized(self)
            {
                for (int i = 0; i < [retry count]; i++) {
                    if([[(RetryBlock*)[retry objectAtIndex:i] word] isEqualToString:[(VOCWebView*)sender word]])
                    {
                        [retry removeObjectAtIndex:i];
                    }
                }
            }
            found = YES;
        }
    }
    if(!found)
    {
        BOOL hasSameWord = NO;
        NSLog(@"didn`t find definition of the word %@,retrying.", [(VOCWebView*)sender word]);
        for (int i = 0; i < [retry count]; i++) {
            if([[(RetryBlock*)[retry objectAtIndex:i] word] isEqualToString:[(VOCWebView*)sender word]])
            {
                hasSameWord = YES;
            }
        }
        if(!hasSameWord)
        {
            RetryBlock *wordToRetry = [[RetryBlock alloc] init];
            [wordToRetry setWord:[(VOCWebView*)sender word]];
            [wordToRetry setTimes:0];
            @synchronized(self)
            {
                [retry addObject:wordToRetry];            
            }
            [wordToRetry release];
        }
    }
    
    flags[[(VOCWebView*)sender threadID]] = NO;
    [sender release];
}
- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
{
    //[self log:@"start to fetch the server`s information"];
}

- (void)webView:(WebView *)sender willPerformClientRedirectToURL:(NSURL *)URL delay:(NSTimeInterval)seconds fireDate:(NSDate *)date forFrame:(WebFrame *)frame
{
    [self log:[NSString stringWithFormat:@"the url will be redirect to %@", URL]];
}

- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
{
    [sender release];
}

- (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)aError forFrame:(WebFrame *)frame
{
    [self error:[NSString stringWithFormat:@"failed to connect to %@ with error %@",[sender mainFrameURL], aError]];
    flags[[(VOCWebView*)sender threadID]] = NO;
    [sender release];
}
@end
